#include "TestCaseManager.h"
#include "TestCase.h"
#include "ReceiveResult.h"
#include "send_pcap.h"
#include "send_pcap_thread.h"


#include <iostream>
#include <stdlib.h>
#include <getopt.h>
#include <fstream>

using namespace std;

//const static int LISTEN_PORT = 10002;
//const static int TIMEOUT = 10;

struct input_param
{
    string xml_path;
    string pcap_path;
    CaseConditions conditions;
    uint32_t fps;
    uint32_t threads;
    string ether;
    int listen_port;
    int timeout;
    string port_cmp_data;
    uint32_t change_port;

    input_param() {fps = 0; threads = 0; listen_port = 10002; timeout=10; change_port=0;}
};

void InitLibnet(int num, const string &device, vector<libnet_t*> &libnets);
void DestroyLibnet(const vector<libnet_t*> &libnets);

/********************************************************************************
*  FUNC     :   输出使用帮助
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void dump_usage()
{
    cout << "Usage: ./apm_test [conditions] [other]  -i case.xml" << endl
         << "       ./apm_test [conditions] [other] -w test.pcap" << endl;

    cout << endl;
    cout << "conditions:" << endl
        << "\t--arch:    需要运行指定硬件平台的case，比如32位还是64位" << endl
        << "\t--os:      需要运行指定操作系的case，比如windows还是linux" << endl
        << "\t--version: 需要运行指定数据库版本的case，比如sqlserver1.0" << endl
        << "\t--charset: 需要运行指定数据库字符集的case，比如utf-8" << endl
        << "\t--program: 需要运行指定客户端的case，比如client1.0" << endl;

    cout << endl;
    cout << "other:" << endl
        << "\t-i:        指定传给测试程序的xml文件" << endl
        << "\t-w:        指定需要测试验证的pcap文件" << endl
        << "\t-l:        指定接收结果的端口号，默认为10002" << endl
        //<< "\t--threads: 指定发送线程数进行压力测试，不验证结果,此时程序会一直运行" << endl
        //<< "\t--fps:     每秒发送包的个数" << endl
        << "\t--cport:   需要修改端口号，例如: tcp:192.168.1.1:1433:1455" << endl
        << "\t--ether:   指定使用的网卡, 不指定时自动选取一个可用的网卡" << endl
        << "\t--checktime:   设置等待解析服务器结果超时时间" << endl;


    exit(-1);
}

/********************************************************************************
*  FUNC     :   解析命令行参数
*  ARGS     :   argc (命令行传入的参数个数)
                argv (命令行传入的参数)
                param (struct input_param, 存储命令行参数)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void parse_opts(int argc, char *argv[], input_param &param)
{
    const string short_opts = "i:w:l:";
    const struct option long_options[] = {
        {"arch", 1, NULL, 'A'},
        {"os", 1, NULL, 'O'},
        {"version", 1, NULL, 'V'},
        {"charset", 1, NULL, 'C'},
        {"program", 1, NULL, 'P'},
        //{"fps", 1, NULL, 'F'},
        //{"threads", 1, NULL, 'T'},
        {"ether", 1, NULL, 'I'},
        {"checktime", 1, NULL, 'E'},
        {"cport", 1, NULL, 'R'},
        {NULL, 0, NULL, 0}
    };

    int next;
    do
    {
        next = getopt_long(argc, argv, short_opts.c_str(), long_options, NULL);
        switch(next)
        {
            case 'i':
                param.xml_path = optarg;
                break;
            case 'w':
                param.pcap_path = optarg;
                break;
            case 'l':
                param.listen_port = atoi(optarg);
                break;
            case 'A':
                param.conditions.arch = optarg;
                break;
            case 'O':
                param.conditions.os = optarg;
                break;
            case 'V':
                param.conditions.version = optarg;
                break;
            case 'C':
                param.conditions.charset = optarg;
                break;
            case 'P':
                param.conditions.program = optarg;
                break;
            //case 'F':
            //    {
            //        int32_t tmp = atoi(optarg);
            //        if(tmp > 0) param.fps = tmp;
            //    }
            //    break;
            //case 'T':
            //    {
            //        int32_t tmp = atoi(optarg);
            //        if(tmp > 0) param.threads = tmp;
            //    }
            //    break;
            case 'I':
                param.ether = optarg;
                break;
            case 'E':
                param.timeout = atoi(optarg);
                break;
            case 'R':
                {
                    char *cp = strrchr(optarg, ':');
                    if(!cp) dump_usage();

                    *cp++ = 0;
                    param.port_cmp_data = string(optarg);
                    param.change_port = atoi(cp);
                }
                break;
            case -1:
                if ((param.xml_path.length() == 0 && param.pcap_path.length() == 0)
                    || (param.xml_path.length() != 0 && param.pcap_path.length() != 0))
                    dump_usage();
                break;
            default:
                dump_usage();
        }
    } while(next != -1);
}

/********************************************************************************
*  FUNC     :   保存解析结果
*  ARGS     :   param (struct input_param, 存储命令行参数)
*  RTN      :   成功: 0,    失败: !0
*  NOTE     :
*******************************************************************************/
int DumpMode(const input_param &param)
{
    TestCase oneCase;
    oneCase.SetConditions(param.conditions);
    //oneCase.SetName("case name");
    oneCase.SetPcap(param.pcap_path);

    //启动接收结果线程
    ReceiveResult result(oneCase, param.listen_port, param.timeout);
    result.setSaveResultMode(true);
    result.Start();
    sleep(2);

    vector<libnet_t*> libnets;
    InitLibnet(1, param.ether, libnets);
    if(libnets.size() < 1)
    {
        return -1;
    }
    //发送pcap数据
    SendPcap sendPcap(oneCase.GetPcap(), param.fps, libnets[0],
            param.port_cmp_data, param.change_port);
    sendPcap.doSend();


    //等待接收结果线程退出
    result.Join();
    DestroyLibnet(libnets);

    cout << oneCase << endl;

    //保存结果到xml中
    uint32_t resNum = oneCase.GetCompareDatasNum();
    cout << "reveive result num: " << resNum << endl;
    string xmlPath = param.pcap_path + ".xml";
    cout << "save result to file: " << xmlPath << endl;

    TestCaseManager mnger(xmlPath);
    mnger.SetTestCase(oneCase);
    mnger.Dump();

    return 0;
}

/********************************************************************************
*  FUNC     :   输出结果
*  ARGS     :   os (输出流，屏幕或文件)
                passNum (成功case的个数)
                norunNum (未运行case的个数)
                mnger (case管理)
                param (命令行输入条件)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void DumpoutResult(ostream &os, int passNum, int norunNum, const TestCaseManager &mnger,
        const input_param &param)
{
    int totalNum = mnger.GetTestCaseNum();
    int failNum = totalNum - passNum - norunNum;
    os << "Total case: " << totalNum << "\tPass: " << passNum << "\tFailed: "
        << failNum << ", not run: " << norunNum << endl;

    if(failNum == 0) return;

    os << "Fail case info:" << endl;
    for(int i = 0; i < totalNum; i++)
    {
        const TestCase &oneCase = mnger.GetTestCase(i);
        if (!oneCase.DoTestCase(param.conditions)) continue;

        if (oneCase.GetPassed()) continue;

        os << "Name: " << oneCase.GetName() << endl;
        os << "Pcap: " << oneCase.GetPcap() << endl;
        os << oneCase.GetReason() << endl << endl;
    }
}

/********************************************************************************
*  FUNC     :   比对解析结果
*  ARGS     :   param (struct input_param, 存储命令行参数)
*  RTN      :   成功: 0,    失败: !0
*  NOTE     :
*******************************************************************************/
int CheckMode(const input_param &param)
{
    //从xml中读取case信息
    TestCaseManager mnger(param.xml_path);
    if(!mnger.Parse())
    {
        cerr << "parse case xml failed" << endl;
        return -1;
    }

    //处理每一个case
    uint32_t num = mnger.GetTestCaseNum();

    vector<libnet_t*> libnets;
    InitLibnet(num, param.ether, libnets);
    if(libnets.size() < num)
    {
        return -1;
    }

    uint32_t passed_num = 0;
    uint32_t norun_num = 0;
    for(uint32_t i = 0; i < num; i++)
    {
        TestCase &oneCase = mnger.GetTestCase(i);
        if (!oneCase.DoTestCase(param.conditions))
        {
            norun_num++;
            continue;
        }

        cout << "check case: " << oneCase.GetName() << endl;

        //启动接收结果线程
        ReceiveResult result(oneCase, param.listen_port, param.timeout);
        result.Start();
        sleep(2);

        //发送pcap数据
        SendPcap sendPcap(oneCase.GetPcap(), param.fps, libnets[i],
                param.port_cmp_data, param.change_port);
        sendPcap.doSend();

        //等待接收结果线程退出
        result.Join();

        if(oneCase.GetPassed())
        {
            cout << "check passed" << endl << endl;
            passed_num++;
        }
        else
        {
            cout << "check failed" << endl << endl;
        }
    }

    DestroyLibnet(libnets);

    //输出结果
    DumpoutResult(cout, passed_num, norun_num, mnger, param);
    string resFile = param.xml_path + ".res";
    ofstream fout;
    fout.open(resFile.c_str(), ios::out);
    DumpoutResult(fout, passed_num, norun_num, mnger, param);
    fout.close();

    return 0;
}

/********************************************************************************
*  FUNC     :   初始化libnet
*  ARGS     :   num (准备num个libnet句柄)
                device (使用网卡设备名,如 eth0,没有设置为"")
                libnets (返回初始化好的libnet句柄)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void InitLibnet(int num, const string &device, vector<libnet_t*> &libnets)
{
    char errBuf[PCAP_ERRBUF_SIZE];
    const char *tmpDevice = NULL;
    if(device.length() != 0)
        tmpDevice = device.c_str();
    for(int i = 0; i < num; i++)
    {
        libnet_t *l = libnet_init(LIBNET_LINK_ADV, tmpDevice, errBuf);
        if(!l)
        {
            cout << "libnet_init failed, " << errBuf <<  endl;
        }
        else
        {
            libnets.push_back(l);
        }
    }
}

/********************************************************************************
*  FUNC     :   释放libnet
*  ARGS     :   libnets (需要释放的libnet句柄)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void DestroyLibnet(const vector<libnet_t*> &libnets)
{
    for(uint32_t i = 0; i < libnets.size(); i++)
    {
        libnet_t *l = libnets[i];
        if(l)
            libnet_destroy(l);
    }
}

/********************************************************************************
*  FUNC     :   多线程发送测试，不验证结果
*  ARGS     :   param (struct input_param, 存储命令行参数)
*  RTN      :   成功: 0,    失败: !0
*  NOTE     :
*******************************************************************************/
int PressureMode(const input_param &param)
{
    //从xml中读取case信息
    TestCaseManager mnger(param.xml_path);
    if(!mnger.Parse())
    {
        cerr << "parse case xml failed" << endl;
        return -1;
    }


    uint32_t caseNum = mnger.GetTestCaseNum();
    uint32_t threadNum = min(caseNum, param.threads);
    cout << "start " << threadNum << " threads" << endl;

    vector<libnet_t*> libnets;
    InitLibnet(threadNum, param.ether, libnets);
    if(libnets.size() < threadNum)
        return -1;

    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex,NULL);

    while(1)
    {
        vector<SendPcapThread*> ths;
        //设置所有case为没有通过状态
        for(uint32_t i = 0; i < caseNum; i++)
        {
            TestCase &tmpCase = mnger.GetTestCase(i);
            tmpCase.SetPassed(false);
        }

        //启动指定的线程数
        for(uint32_t i = 0; i < threadNum; i++)
        {
            SendPcapThread *th = new SendPcapThread(mnger, mutex, param.conditions,
                param.fps, libnets[i]);
            th->setChangePortInfo(param.port_cmp_data, param.change_port);
            th->Start();
            ths.push_back(th);
        }

        //等待所有线程退出
        for(uint32_t i = 0; i<ths.size(); i++)
        {
            ths[i]->Join();
            delete ths[i];
        }
    }
    pthread_mutex_destroy(&mutex);

    DestroyLibnet(libnets);

    return 0;
}

/********************************************************************************
*  FUNC     :   主函数入口
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
int main (int argc, char *argv[])
{
    //第1步：读取输入配置
    input_param param;
    parse_opts(argc, argv, param);

    /*char buf[PATH_MAX];
    if(param.xml_path.length() != 0)
    {
        realpath(param.xml_path.c_str(), buf);
        param.xml_path = string(buf);
        cout << param.xml_path << endl;
    }

    if(param.pcap_path.length() != 0)
    {
        realpath(param.pcap_path.c_str(), buf);
        param.pcap_path = string(buf);
        cout << param.pcap_path << endl;
    }

    //获取程序工作目录
    realpath(argv[0], buf);
    char *cp = strrchr(buf, '/');
    *cp++ = 0;
    //设置工作目录
    chdir(buf);*/

    if(param.xml_path.length() != 0)
    {
        //if(param.threads == 0)
            return CheckMode(param);
        //else
        //    return PressureMode(param);
    }

    if(param.pcap_path.length() != 0)
        return DumpMode(param);

    return 0;
}
